#include "http_connection.h"
#include "http_response.h"
#include "sso_dbus.h"
#include "global_setting.h"
#include "ott_request.h"

#include <QHostAddress>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>
#include <QFile>
#include <QSettings>
#include <QDebug>
#include <QDir>
#include <QCoreApplication>

// 4kb大小
#define HTTP_REQUEST_MAX_LENGTH 4096

using std::placeholders::_1;
using std::placeholders::_2;

HttpConnection::HttpConnection(qintptr sockFd, QObject *parent)
    : QObject (parent)
    , m_tcpSocket(new QTcpSocket)
{
    initConnect();
    m_tcpSocket->setSocketDescriptor(sockFd);
    onConnected();
}

HttpConnection::~HttpConnection()
{
    qInfo() << "--------------------- delete HttpConnection";
    m_tcpSocket->deleteLater();
}

/**
 * @brief HttpConnection::initConnect 初始化信号槽连接
 */
void HttpConnection::initConnect()
{
    connect(m_tcpSocket, &QTcpSocket::stateChanged, this, &HttpConnection::onStateChanged);

#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
    connect(m_tcpSocket, &QTcpSocket::errorOccurred, this, &HttpConnection::onError);
#else
    connect(m_tcpSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error),
            this, &HttpConnection::onError);
#endif

    connect(m_tcpSocket, &QTcpSocket::disconnected, this, &HttpConnection::onDisconnected);
    connect(m_tcpSocket, &QTcpSocket::readyRead, this, &HttpConnection::onReadyRead);
}

/**
 * @brief HttpConnection::localIpPort 获取本地ip端口信息
 * @return 返回本地ip端口信息
 */
QString HttpConnection::localIpPort() const
{
    return m_tcpSocket->localAddress().toString() + ":" + QString::number(m_tcpSocket->localPort());
}

/**
 * @brief HttpConnection::peerIpPort 获取对端ip端口信息
 * @return 返回对端ip端口信息
 */
QString HttpConnection::peerIpPort() const
{
    return m_tcpSocket->peerAddress().toString() + ":" + QString::number(m_tcpSocket->peerPort());
}

void HttpConnection::onStateChanged(QAbstractSocket::SocketState socketState)
{
    Q_UNUSED(socketState)

#ifdef QT_DEBUG
    qDebug() << socketState;
#endif
}

void HttpConnection::onError(QAbstractSocket::SocketError socketError)
{
    qWarning() << socketError;
}

/**
 * @brief HttpConnection::onConnected 处理tcp socket的连接
 */
void HttpConnection::onConnected()
{
    qInfo() << localIpPort() << " -> "
              << peerIpPort() << " is "
              << "UP";
}

/**
 * @brief HttpConnection::onDisconnected 处理tcp socket的断开
 */
void HttpConnection::onDisconnected()
{
    qInfo() << localIpPort() << " -> "
              << peerIpPort() << " is "
              << "DOWN";
    deleteLater(); //销毁自己
}

/**
 * @brief HttpConnection::onReadyRead 读取tcp socket的数据
 */
void HttpConnection::onReadyRead()
{
    Q_ASSERT(m_tcpSocket == sender());

    // 如果请求数据大小超过 HTTP_REQUEST_MAX_LENGTH ,则强制断开tcp连接, 防止恶意攻击
    if (m_tcpSocket->bytesAvailable() >= HTTP_REQUEST_MAX_LENGTH || m_tcpSocket->bytesAvailable() + m_buffer.size() >= HTTP_REQUEST_MAX_LENGTH) {
        qWarning() << "The HTTP request data is too large...........";
        m_tcpSocket->abort();
        return;
    }

    m_buffer.append(m_tcpSocket->readAll());
    m_httpContext.parseRequest(m_buffer);
    //判断是否消息接收完整
    if (m_httpContext.gotAll()) {
        qInfo() << "-----------------------------------HTTP REQUEST ----------------------------";
        qInfo() << qPrintable(QString(m_buffer).replace("\r", ""));
        qInfo() << "-----------------------------------------------------------------------------";

        processRequest();
        m_buffer.clear();
        m_httpContext.reset();
    }
}

/**
 * @brief HttpConnection::processRequest 处理HTTP请求
 */
void HttpConnection::processRequest()
{
    const HttpRequest &request = m_httpContext.request();

    if (request.getMethod() == "GET") { // 处理GET请求
        processHttpGET();
    } else if (request.getMethod() == "POST") { //处理POST请求
        processHttpPOST();
    } else if (request.getMethod() == "OPTIONS") {
        HttpResponse response;
        response.setCorsStatus(false);
        // 处理头部字段的添加
        response.addHeader("Server", "SSO Server 1.0");
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
        response.addHeader("Access-Control-Max-Age", "3600");
        response.addHeader("Access-Control-Allow-Headers", "*");
        response.addHeader("Allow", "GET, POST, OPTIONS");
        response.setContentType("text/plain;charset=UTF-8");
        response.setStatusCode(HttpResponse::k200Ok);
        response.setStatusMessage(HttpResponse::statusCodeToString(HttpResponse::k200Ok).toUtf8());
        response.setCloseConnection(true);
        QByteArray buffer;
        //把HTTP响应消息写入到buffer
        response.appendToBuffer(buffer);
        m_tcpSocket->write(buffer);
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
    } else {
        QByteArray data = HttpResponse::makeNotFoundResponse();
        m_tcpSocket->write(data);
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
    }
}

/**
 * @brief HttpConnection::processHttpGET 处理HTTP GET请求
 */
void HttpConnection::processHttpGET()
{
    const HttpRequest &request = m_httpContext.request();
    if (request.getUrl() == "/" || request.getUrl() == "/index.html") {
        HttpResponse response;
        response.setContentType("text/html;charset=UTF-8");
        response.addHeader("Server", "SSO Server 1.0");
        response.setStatusCode(HttpResponse::k200Ok);
        response.setStatusMessage(HttpResponse::statusCodeToString(HttpResponse::k200Ok).toUtf8());
        response.setBody(makeIndexHtml().toUtf8());
        response.setCloseConnection(true);
        QByteArray buffer;
        response.appendToBuffer(buffer);
        m_tcpSocket->write(buffer);
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
    } else if (request.getUrl() == "/iam/userinfo") { // 处理获取用户信息的接口
        const QJsonObject &userInfoObject = SsoService::getInstance()->getUserList();
        QJsonObject rootObject;
        if (userInfoObject.isEmpty()) { // 当前域账户信息为空的情况
            rootObject.insert("code", NoDomainAccount);
            rootObject.insert("msg", "当前没有域账户登陆");
            rootObject.insert("username", QString());
            rootObject.insert("usericon", QString());
        } else {
            QString userName = userInfoObject.value("name").toString();
            int pos = userName.indexOf('@');
            if (pos != -1) {
                // 去掉'@'符号之后字符串
                userName = userName.left(pos);
            }
            rootObject.insert("code", NoError);
            rootObject.insert("msg", "");
            rootObject.insert("username", userName);
            // 读取用户图像数据
            QFile file(userInfoObject.value("icon").toString());
            if (file.open(QFile::ReadOnly)) {
                // 图像数据采用base64编码
                QByteArray iconData = file.readAll().toBase64();
                rootObject.insert("usericon", QString(iconData));
            } else {
                rootObject.insert("usericon", QString());
            }
        }

        m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, QJsonDocument(rootObject).toJson(QJsonDocument::Compact)));
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
    } else {
        QByteArray data = HttpResponse::makeNotFoundResponse();
        m_tcpSocket->write(data);
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
    }
}

/**
 * @brief HttpConnection::processHttpPOST 处理HTTP POST 请求
 */
void HttpConnection::processHttpPOST()
{
    const HttpRequest &request = m_httpContext.request();

    if (request.getUrl() == "/iam/getott") { //处理获取OTT的接口
        const QByteArray &body = request.getBody();
        QJsonParseError parseError;
        QJsonDocument jsonDocument(QJsonDocument::fromJson(body, &parseError));
        if (parseError.error != QJsonParseError::NoError
                || !jsonDocument.isObject()
                || !jsonDocument.object().contains("app_id")
                || !jsonDocument.object().contains("app_name")
                || !jsonDocument.object().contains("public_key")) {
            // 生成返回错误反馈信息的json对象
            QJsonObject rootObject;
            rootObject.insert("code", JsonError);
            rootObject.insert("msg", "请求体json格式错误");
            rootObject.insert("ott", QString());

            m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, QJsonDocument(rootObject).toJson(QJsonDocument::Compact)));
            m_tcpSocket->disconnectFromHost(); //断开tcp连接
            return;
        }

        const QJsonObject &requestObject = jsonDocument.object();

        // 处理二次确认的情况,这里需要弹出UI供用户操作
        if (GlobalSetting::instance()->bsSecondConfirm()) {
            // 获取本次请求索引值
            const qint64 requestIndex = SsoService::getRequestIndex() + 1;
            connect(SsoService::getInstance(), &SsoService::Response, this, &HttpConnection::onGetott);
            connect(SsoService::getInstance(), &SsoService::ResponseCancel,
                    this, std::bind(&HttpConnection::onResponseCancel, this, requestIndex, std::placeholders::_1));
            // 请求显示UI窗口供用户选择
            SsoService::getInstance()->AuthDialogShow(requestObject.value("app_id").toString(),
                                                      requestObject.value("app_name").toString(),
                                                      requestObject.value("public_key").toString());
        } else {
            // 获取用户信息
            const QJsonObject &userInfoObject = SsoService::getInstance()->getUserList();

            QJsonObject newObject;
            newObject.insert("uid", userInfoObject.value("uid").toInt());
            newObject.insert("app_id", requestObject.value("app_id").toString());
            newObject.insert("public_key", requestObject.value("public_key").toString());

            // 直接请求cache-udcp 获取OTT
            OttRequest::getInstance()->requestBrowserOtt(QJsonDocument(newObject).toJson(QJsonDocument::Compact),
                                                         std::bind(&HttpConnection::getOttCallback, this, _1));
        }

    } else {
        QByteArray data = HttpResponse::makeNotFoundResponse();
        m_tcpSocket->write(data);
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
    }
}

/**
 * @brief HttpConnection::onGetott 响应获取OTT的槽函数
 * @param jsonStr json字符串
 */
void HttpConnection::onGetott(const QString &jsonStr)
{
    if (!m_tcpSocket || m_tcpSocket->state() != QTcpSocket::ConnectedState) {
        qWarning() << "tcp socket disconnected !!!";
        return;
    }

    QJsonParseError parseError;
    QJsonDocument jsonDocument(QJsonDocument::fromJson(jsonStr.toUtf8(), &parseError));
    // 判断json的格式是否错误
    if (parseError.error != QJsonParseError::NoError
            || !jsonDocument.object().contains("code")
            || !jsonDocument.object().contains("msg")
            || !jsonDocument.object().contains("ott")) {
        QJsonObject rootObject;
        rootObject.insert("code", JsonError);
        rootObject.insert("msg", "JSON error sent by cache udcp !!!");
        rootObject.insert("ott", QString());
        QByteArray jsonData = QJsonDocument(rootObject).toJson(QJsonDocument::Compact);
        // 发送反馈错误信息的json
        m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, jsonData));
        m_tcpSocket->disconnectFromHost();
        return;
    }

    // code不为0的时候输出json，方便排查错误信息
    if (jsonDocument.object().value("code").toInt() != 0) {
        qWarning() << qPrintable(jsonStr);
    }

    m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, jsonStr.toUtf8()));
    m_tcpSocket->disconnectFromHost();
}

/**
 * @brief HttpConnection::onResponseCancel 响应SsoService 返回信号的槽函数
 * @param code 错误码
 */
void HttpConnection::onResponseCancel(qint64 requestIndex, int code)
{
    if (code == AuthCancel) {
        // 生成返回错误反馈信息的json对象
        QJsonObject rootObject;
        rootObject.insert("code", UserCancelAuth);
        rootObject.insert("msg", "已登录用户拒绝授权");
        rootObject.insert("ott", QString());

        m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, QJsonDocument(rootObject).toJson(QJsonDocument::Compact)));
        m_tcpSocket->disconnectFromHost(); //断开tcp连接
        qInfo() << "已登录用户拒绝授权, code: " << code;
    } else if (code == AuthInterrupt) {
        // 判断是否是最后一次请求, 不是最后一次才处理中断
        if (requestIndex != SsoService::getRequestIndex()) {
            // 生成返回错误反馈信息的json对象
            QJsonObject rootObject;
            rootObject.insert("code", UserAuthInterrupt);
            rootObject.insert("msg", "授权被其他应用中断，请您重新发起申请");
            rootObject.insert("ott", QString());

            m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, QJsonDocument(rootObject).toJson(QJsonDocument::Compact)));
            m_tcpSocket->disconnectFromHost(); //断开tcp连接
            qInfo() << "授权被其他应用中断，请您重新发起申请，code: " << code;
        }
    }
}

/**
 * @brief HttpConnection::getOttCallback 请求获取OTT的回调函数
 * @param rootObject json对象
 */
void HttpConnection::getOttCallback(const QJsonObject &rootObject)
{
    if (!rootObject.contains("code")
            || !rootObject.contains("msg")
            || !rootObject.contains("ott")) {
        QJsonObject newObject;
        newObject.insert("code", JsonError);
        newObject.insert("msg", "JSON error sent by cache udcp !!!");
        newObject.insert("ott", QString());
        QByteArray jsonData = QJsonDocument(newObject).toJson(QJsonDocument::Compact);
        // 发送反馈错误信息的json
        m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, jsonData));
        m_tcpSocket->disconnectFromHost();
        return;
    }

    QByteArray jsonData = QJsonDocument(rootObject).toJson(QJsonDocument::Compact);
    // code不为0的时候输出json，方便排查错误信息
    if (rootObject.value("code").toInt() != 0) {
        qWarning() << qPrintable(jsonData);
    }

    m_tcpSocket->write(HttpResponse::makeJsonResponse(HttpResponse::k200Ok, jsonData));
    m_tcpSocket->disconnectFromHost();
}

/**
 * @brief HttpConnection::makeIndexHtml 用于构造一个index.html
 * @return 返回一个index.html
 */
QString HttpConnection::makeIndexHtml() const
{
    QString indexHtml = "<!DOCTYPE html>"
    "<html>"
    "<head>"
        "<title>SSO Server</title>"
    "</head>"
    "<body>"
        "<style type=\"text/css\" media=\"screen\">"
            "#page_one {"
                "background-color: lightgray;"
                "border-color: gray;"
                "border-radius: 8px;"
                "border-width: 1px;"
                "border-style: solid;"
            "}"
        "</style>"

        "<h1>单点登陆本地HTTP服务器</h1>"
        "<h2>功能说明</h2>"
        "<div id=\"page_one\">"
            "<ol>"
                "<li>这个HTTP服务器只支持GET和POST方法</li>"
                "<li>用于browser应用的认证</li>"
            "</ol>"
        "</div>"
    "</body>"
    "</html>";
    return indexHtml;
}
